package com.chushouya.aop;

import com.chushouya.common.support.DevOption;
import com.chushouya.common.support.RequestHeaders;
import com.general.framework.core.exception.CodeEnum;
import com.general.framework.core.exception.Ex;
import com.general.framework.core.lang.Dates;
import com.general.framework.core.lang.Jsons;
import com.general.framework.core.lang.Strings;
import com.general.framework.core.utils.StandaloneSignatureUtils;
import com.chushouya.manager.dao.entity.PartnerEntity;
import com.chushouya.manager.service.common.PartnerApiService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

@Component
@Aspect
@Slf4j
public class SignValidateAspect {

    @Resource
    private PartnerApiService partnerApiService;

    @Around("@annotation(com.chushouya.common.annotations.SignValidate)")
    public Object channelAuth(ProceedingJoinPoint pjp) throws Throwable {

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(requestAttributes)) {
            return pjp.proceed();
        }

        HttpServletRequest request = requestAttributes.getRequest();
        final String partnerId = request.getHeader(RequestHeaders.PARTNER_ID);
        final String timestamp = request.getHeader(RequestHeaders.TIMESTAMP);
        final String sign = request.getHeader(RequestHeaders.SIGN);
        // 开发者模式
        if (DevOption.isDevModel() && "skip-sign".equals(sign)) {
            log.info("===>开发者模式，跳过签名验证：{}", sign);
            return pjp.proceed();
        }
        this.headerValidate(partnerId, timestamp, sign);

        // 验证时间戳请求时效
        this.validateTimestamp(timestamp);

        // 验证合作商存在性和状态
        final String appKey = this.validatePartner(partnerId);

        // 验证签名
        this.validateSign(pjp, timestamp, appKey, sign);

        return pjp.proceed();
    }

    private void validateSign(ProceedingJoinPoint pjp, final String timestamp, final String appKey, final String sign) {
        final Object args = pjp.getArgs()[0];
        Map<String, Object> paramMap = Jsons.parseObject(Jsons.toJsonString(args), Map.class);
        
        // 使用 StandaloneSignatureUtils 进行签名验证
        StandaloneSignatureUtils.SignatureResult result = StandaloneSignatureUtils.verifySignature(
            paramMap, timestamp, appKey, sign);
        
        log.debug("request args: {}", Jsons.toJsonString(args));
        log.debug("partner sign: {}, calculated sign: {}", sign, result.getCalculatedSign());
        log.debug("signature raw string: {}", result.getRawString());
        
        if (!result.isVerified()) {
            log.error("签名验证失败 - request args: {}", Jsons.toJsonString(args));
            log.error("partner sign: {}, calculated sign: {}", sign, result.getCalculatedSign());
            log.error("signature raw string: {}", result.getRawString());
            throw Ex.business(CodeEnum.ILLEGALITY_OPERATION.getCode(), CodeEnum.ILLEGALITY_OPERATION.getMessage().concat(":签名错误"));
        }
    }

    private void headerValidate(final String partnerId, final String timestamp, final String sign) {
        if (Strings.isEmpty(partnerId)) {
            throw Ex.business(CodeEnum.VIOLATION_ERROR.getMessage().concat(Strings.format(":header缺少{}参数", RequestHeaders.PARTNER_ID)));
        }
        if (Strings.isEmpty(timestamp)) {
            throw Ex.business(CodeEnum.VIOLATION_ERROR.getMessage().concat(Strings.format(":header缺少{}参数", RequestHeaders.TIMESTAMP)));
        }
        if (Strings.isEmpty(sign)) {
            throw Ex.business(CodeEnum.VIOLATION_ERROR.getMessage().concat(Strings.format(":header缺少{}参数", RequestHeaders.SIGN)));
        }
    }

    private void validateTimestamp(String timestamp) {
        final Date requestTime;
        try {
            requestTime = Dates.parseDate(Long.valueOf(timestamp));
        } catch (Exception e) {
            log.error("错误的时间戳格式:{}", timestamp);
            throw Ex.business(CodeEnum.VIOLATION_ERROR.getCode(), CodeEnum.VIOLATION_ERROR.getMessage().concat(Strings.format(":header参数{}格式错误", RequestHeaders.TIMESTAMP)));
        }
        final Date timeNow = Dates.getTimeNow();
        final long time1 = Dates.addMinutes(timeNow, -5).getTime();
        final long time2 = Dates.addMinutes(timeNow, 5).getTime();
        if (requestTime.getTime() < time1 || requestTime.getTime() > time2) {
            log.error("时间戳失效 传入时间戳：{}，当前时间戳：{}",timestamp, timeNow.getTime());
            throw Ex.business(CodeEnum.FORBIDDEN.getCode(), CodeEnum.FORBIDDEN.getMessage().concat(Strings.format(":header参数{}失效", RequestHeaders.TIMESTAMP)));
        }
    }

    private String validatePartner(final String partnerId) {
        if (Strings.isEmpty(partnerId)) {
            throw Ex.business(CodeEnum.VIOLATION_ERROR.getCode(), CodeEnum.VIOLATION_ERROR.getMessage().concat(Strings.format(":header缺少{}参数", RequestHeaders.PARTNER_ID)));
        }
        PartnerEntity partnerEntity = partnerApiService.getCachePartnerInfo(Long.valueOf(partnerId));
        if (Objects.isNull(partnerEntity)) {
            throw Ex.business(CodeEnum.ILLEGALITY_OPERATION.getCode(), CodeEnum.ILLEGALITY_OPERATION.getMessage().concat(":合作商不存在"));
        }
        if (!Objects.equals(partnerEntity.getStatus(), 1)) {
            throw Ex.business(CodeEnum.ILLEGALITY_OPERATION.getCode(), CodeEnum.ILLEGALITY_OPERATION.getMessage().concat(":合作商已被禁用"));
        }
        return partnerEntity.getAppKey();
    }

}
